package com.fuchuang.seckillsystem.filter;

import com.fuchuang.seckillsystem.utils.ResponseUtils;
import com.fuchuang.seckillsystem.vo.RequestLimit;
import com.fuchuang.seckillsystem.vo.RespBean;
import com.fuchuang.seckillsystem.vo.RespBeanEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * 对同一ip请求过于频繁进行拦截
 */
@Component
public class IpRepeatRequestFilter extends OncePerRequestFilter {
    
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    
    @Autowired
    @Qualifier("request")
    private DefaultRedisScript<Long> redisScript;
    
    @Autowired
    private RequestLimit requestLimit;
    
    private static final List<String> limitPath = new ArrayList<>();
    
    static {
        //添加ip拦截器需要拦截的路径
        Collections.addAll(limitPath, "/seckill/path", "/seckill/buy");
    }
    
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String ip = getIpFromRequest(request);
        String servletPath = request.getServletPath();
//        Integer requestCount = (Integer) redisTemplate.opsForValue().get(KeyConstants.REQUEST_REPEAT + ip);
//        if (requestCount != null && requestCount > requestLimit.getCount()) {
//            ResponseUtils.result(response, RespBean.error(RespBeanEnum.REQUEST_ERROR));
//            return;
//        } else if (requestCount == null) {
//            redisTemplate.opsForValue().set(KeyConstants.REQUEST_REPEAT + ip, 1, requestLimit.getExpireTime(), TimeUnit.SECONDS);
//        } else {
//            redisTemplate.opsForValue().increment(KeyConstants.REQUEST_REPEAT + ip);
//        }
        //如果请求在拦截名单中
        if (limitPath.contains(servletPath)) {
            //采用redis滑动窗口算法实现ip限制
            //INCR 和 EXPIRE 命令操作应该在一个原子操作中提交，以保证每个 key 都正确设置了过期时间，不然会有 key 值无法自动删除而导致的内存溢出
            long now = System.currentTimeMillis();
            String oldest = String.valueOf(now - requestLimit.getExpireTime() * 1000);
            String score = String.valueOf(now);
            Long execute = stringRedisTemplate.execute(redisScript, Arrays.asList(ip + ":" + servletPath), oldest, score, String.valueOf(requestLimit.getCount()), score);
            if (execute != 1) {
                ResponseUtils.result(response, RespBean.error(RespBeanEnum.REQUEST_ERROR));
                return;
            }
        }
        filterChain.doFilter(request, response);
    }
    
    private String getIpFromRequest(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}

